
package org.movee.spring.data.hugegraph.config;

import com.baidu.hugegraph.driver.HugeClient;
import org.movee.spring.data.hugegraph.convert.MappingHugeConverter;
import org.movee.spring.data.hugegraph.mapping.HugeMappingContext;
import org.movee.spring.data.hugegraph.query.HugeTemplate;
import org.movee.spring.data.hugegraph.schema.SchemaInitiator;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.annotation.Persistent;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

@Slf4j
public abstract class AbstractHugeConfiguration {

    public abstract HugeConfig getGremlinConfig();

    @Bean
    public HugeClient hugeClient() {
        return new HugeClient(HugeClient.builder(getGremlinConfig().getUrl(),
                getGremlinConfig().getGraph()));
    }

    @Bean
    public HugeMappingContext gremlinMappingContext() throws ClassNotFoundException {
        final HugeMappingContext context = new HugeMappingContext();
        context.setInitialEntitySet(this.getInitialEntitySet());
        return context;
    }

    @Bean
    public MappingHugeConverter mappingGremlinConverter(HugeMappingContext mappingContext) {

        return new MappingHugeConverter(mappingContext);
    }

    @Bean
    public SchemaInitiator schemaInitiator(HugeClient hugeClient, HugeMappingContext mappingContext) {

        return new SchemaInitiator(hugeClient, mappingContext);
    }

    @Bean
    public HugeTemplate hugeTemplate(HugeClient hugeClient,
                                        MappingHugeConverter converter,
                                        SchemaInitiator schemaInitiator) {
        return new HugeTemplate(hugeClient, converter, schemaInitiator);
    }

    private Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
        final Set<Class<?>> entitySet = new HashSet<>();

        for (final String basePackage : this.getMappingBasePackages()) {
            entitySet.addAll(this.scanEntities(basePackage));
        }

        return entitySet;
    }

    private Collection<String> getMappingBasePackages() {
        final Package basePackage = this.getClass().getPackage();
        return Collections.singleton(basePackage == null ? null : basePackage.getName());
    }

    private Set<Class<?>> scanEntities(@NonNull String basePackage) throws ClassNotFoundException {
        if (!StringUtils.hasText(basePackage)) {
            return Collections.emptySet();
        }

        final Set<Class<?>> entitySet = new HashSet<>();
        final ClassPathScanningCandidateComponentProvider provider =
                new ClassPathScanningCandidateComponentProvider(false);

        provider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));

        for (final BeanDefinition candidate : provider.findCandidateComponents(basePackage)) {
            final String className = candidate.getBeanClassName();
            Assert.notNull(this.getClass().getClassLoader(), "Class loader cannot be null");

            entitySet.add(ClassUtils.forName(className, this.getClass().getClassLoader()));
        }

        return entitySet;
    }

}
